//
// Created by wxd on 2025/8/5.
//

#include "BaseGeometry.h"

BaseGeometry::~BaseGeometry() {
    if (m_VAO != nullptr) {
        delete m_VAO;
    }

    if (m_EBO != nullptr) {
        delete m_EBO;
    }

    if (!m_VboMap.empty()) {
        for (auto& pair : m_VboMap) {
            if (pair.second.vbo != nullptr) {
                pair.second.vbo->destroy();
            }
        }
    }
}

GLuint BaseGeometry::getVAOID() const {
    if (m_VAO == nullptr) {
        return 0;
    }
    return m_VAO->getVAO();
}

GLuint BaseGeometry::getEBOID() const {
    if (m_EBO == nullptr) {
        return 0;
    }
    return m_EBO->getEBO();
}

GLuint BaseGeometry::getVBO(VBOType vboType) {
    auto it = m_VboMap.find(vboType);
    if (it != m_VboMap.end() && it->second.vbo != nullptr) {
        return it->second.vbo->getVBO();
    }
    return 0;
}

size_t BaseGeometry::getIndicesCount() const {
    return m_IndicesCount;
}

size_t BaseGeometry::getVerticesCount() {
    return m_VerticesCount;
}

VAO *BaseGeometry::getVAO() const {
    return m_VAO;
}

EBO *BaseGeometry::getEBO() const {
    return m_EBO;
}

std::string BaseGeometry::getTraceId() const {
    return m_GeoTraceId;
}

void BaseGeometry::initVAO() {
    m_VAO = new VAO();
}

void BaseGeometry::initEBO(const std::vector<unsigned int> &indices, GLenum dataType) {
    m_EBO = new EBO(indices, dataType);
    m_EBO->bind();
    m_IndicesCount = indices.size();
}

void BaseGeometry::initVBO(const std::vector<float> &data, VBOType vboType, int comp, int location, GLenum dataType) {
    VBOStruct vboStruct;
    vboStruct.vbo = new VBO(data, dataType, comp, location);
    vboStruct.vbo->bindLocation(m_VAO->getVAO());
    vboStruct.type = vboType;
    m_VboMap.emplace(vboType, std::move(vboStruct)); // 使用 emplace 避免拷贝
    if (vboType == POS) {
        m_VerticesCount = data.size();
    }
}

void BaseGeometry::bindVBO(VBOType vboType) {
    auto it = m_VboMap.find(vboType);
    if (it == m_VboMap.end()) {
        return;  // 或者抛出异常，根据你的错误处理策略
    }

    // 使用引用，避免任何拷贝
    VBOStruct& vboStruct = it->second;

    // 添加空指针检查
    if (vboStruct.vbo) {
        vboStruct.vbo->bindLocation(m_VAO->getVAO());
    }
}

void BaseGeometry::draw() {
    if (m_DrawType == DrawType::DRAW_ARRAYS) {
        glDrawArrays(m_DrawMode, 0, static_cast<int>(getVerticesCount()));
    } else if (m_DrawType == DrawType::DRAW_ELEMENTS) {
        glDrawElements(m_DrawMode, static_cast<int>(m_IndicesCount), GL_UNSIGNED_INT, 0);
    }
}

void BaseGeometry::setCol(const int location, const std::vector<glm::vec3> &cols) {
    const auto size = cols.size();
    std::vector<float> col_vector;
    col_vector.reserve(3 * size);
    for (int i = 0; i < size; i++) {
        auto col = cols[i];
        col_vector.push_back(col.r);
        col_vector.push_back(col.g);
        col_vector.push_back(col.b);
    }
    initVBO(col_vector, COL, 3, location, GL_STATIC_DRAW);
}

void BaseGeometry::setCol(const int location, const std::vector<glm::vec4> &cols) {
    const auto size = cols.size();
    std::vector<float> col_vector;
    col_vector.reserve(4 * size);
    for (int i = 0; i < size; i++) {
        auto col = cols[i];
        col_vector.push_back(col.r);
        col_vector.push_back(col.g);
        col_vector.push_back(col.b);
        col_vector.push_back(col.a);
    }
    initVBO(col_vector, COL, 4, location, GL_STATIC_DRAW);
}

void BaseGeometry::setUV(const int location, const std::vector<glm::vec2> &uvs) {
    const auto size = uvs.size();
    std::vector<float> uv_vector;
    uv_vector.reserve(2 * size);
    for (int i = 0; i < size; i++) {
        auto uv = uvs[i];
        uv_vector.push_back(uv.x);
        uv_vector.push_back(uv.y);
    }
    initVBO(uv_vector, UV, 2, location, GL_STATIC_DRAW);
}

void BaseGeometry::setNormal(const int location, const std::vector<glm::vec3> &normals) {
    const auto size = normals.size();
    std::vector<float> normal_vector;
    normal_vector.reserve(3 * size);
    for (int i = 0; i < size; i++) {
        auto normal = normals[i];
        normal_vector.push_back(normal.x);
        normal_vector.push_back(normal.y);
        normal_vector.push_back(normal.z);
    }
    initVBO(normal_vector, NORMAL, 3, location, GL_STATIC_DRAW);
}

void BaseGeometry::setPos(const int location, const std::vector<glm::vec2> &pos) {
    const auto size = pos.size();
    std::vector<float> pos_vector;
    pos_vector.reserve(2 * size);
    for (int i = 0; i < size; i++) {
        auto p = pos[i];
        pos_vector.push_back(p.x);
        pos_vector.push_back(p.y);
    }
    initVBO(pos_vector, POS, 2, location, GL_STATIC_DRAW);
}

void BaseGeometry::setPos(const int location, const std::vector<glm::vec3> &pos) {
    const auto size = pos.size();
    std::vector<float> pos_vector;
    pos_vector.reserve(3 * size);
    for (int i = 0; i < size; i++) {
        auto p = pos[i];
        pos_vector.push_back(p.x);
        pos_vector.push_back(p.y);
        pos_vector.push_back(p.z);
    }
    initVBO(pos_vector, POS, 3, location, GL_STATIC_DRAW);
}

void BaseGeometry::setPos(const int location, const std::vector<glm::vec4> &pos) {
    const auto size = pos.size();
    std::vector<float> pos_vector;
    pos_vector.reserve(4 * size);
    for (int i = 0; i < size; i++) {
        auto p = pos[i];
        pos_vector.push_back(p.x);
        pos_vector.push_back(p.y);
        pos_vector.push_back(p.z);
        pos_vector.push_back(p.w);
    }
    initVBO(pos_vector, POS, 4, location, GL_STATIC_DRAW);
}

void BaseGeometry::setIndices(const std::vector<glm::uvec3> &indices) {
    const auto size = indices.size();
    std::vector<unsigned int> indices_vector;
    indices_vector.reserve(3 * size);
    for (int i = 0; i < size; i++) {
        auto face = indices[i];
        indices_vector.push_back(face.x);
        indices_vector.push_back(face.y);
        indices_vector.push_back(face.z);
    }
    initEBO(indices_vector, GL_STATIC_DRAW);
    m_DrawType = DrawType::DRAW_ELEMENTS;
}
